home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9207.ZIP
/
QUICKT.ZIP
/
NOTES.QT
Wrap
Text File
|
1992-05-26
|
21KB
|
380 lines
_PROGRAMMING QUICKTIME_
by Aaron Walsh
PROGRAMMER NOTES:
Once initialized, the user is asked to select a QuickTime
movie file from disk. GetMovie() demonstrates the use of the new
StandardGetFilePreview() routine. This call provides the user
with a thumbnail view (preview) of selected files, as mentioned
earlier. Under System 7, use the call: void
StandardGetFilePreview (FileFilterProcPtr fileFilter, short
numTypes, SFTypeList typeList, StandardFileReply *fReply).
StandardGetFilePreview() corresponds to the older
StandardGetFile() routine documented in Inside Macintosh, Volume
VI with the exception that it provides file preview capabilities.
After execution, fReply.good is used to determine if a valid
selection was made, or if the Cancel button was hit. If a valid
selection was made, fReply contains various file information for
the selected file. Under System 7 this information is returned in
the form of an FSSpec, which is used to reference the movie file
in subsequent calls to the Movie Toolbox. Under System 6,
StandardGetFilePreview() is not available. Instead,
SFGetFilePreview() is used and an FSSpec is manually generated
from the older style reply record.
With the file selected, we enter PlayMovie(), which uses the
FSSpec record to access the movie file and prepare it for
playing. Prior to being played, the movie file must be opened
using OSErr OpenMovieFile (const FSSpec *fileSpec, short
*resRefNum, char permission) where fileSpec contains the volume
reference number, directory ID and file name of our selected
movie. OpenMovieFile() uses this information to open the movie
file's resource fork with the requested permission. Use
permission of 0 (zero) when playing a movie. If the movie file is
opened with no error, a reference number to its resource fork is
returned in resRefNum. We use resRefNum to create a movie from
the opened file with OSErr NewMovieFromFile (Movie *theMovie,
short resRefNum, short *resId, StringPtr resName, short
newMovieFlags, Boolean *dataRefWasChanged).
Given a resource reference number in resRefNum and nil in
both resId and resName, NewMovieFromFile() locates the first
'moov' resource in the open resource fork and resolves its data
references. The resulting movie is accessible through theMovie.
If we wanted to make a movie from a specific 'moov' resource, we
would utilize the resId parameter. Instead, we pass nil, which
tells NewMovieFromFile() to use the first 'moov' resource found.
Since we will neither change nor display the name of the
'moov' resource, pass nil for resName. The newMovieFlags and
dataRefWasChanged provide a mechanism for further controlling the
properties of the newly created movie. We will not utilize these
parameters when playing our movie.
After creating a movie from the file reference, prepare the
display window in which it will ultimately be viewed. Obtain the
movie's bounding box (a rectangle) with GetMovieBox(). The top
left corner of the bounding box should be set to [0,0] using the
OffsetRect() call. This ensures proper placement of the movie
within a display window, created with a call to NewCWindow().
Passing the movie's bounding box to NewCWindow() ensures that the
display window is the proper height and width to accommodate our
movie with void GetMovieBox (Movie m, Rect *boxRect) and void
SetMovieBox (Movie m,const Rect *boxRect).
A call to SetPort() makes our display window the current
graphics port. A subsequent call to SetMovieGWorld() ties the
movie's GWorld to that of our display window. When the movie is
played, it will appear in our display window. Pass nil for both
port and gdh to set the movie's GWorld to the current port and
graphics device, void SetMovieGWorld (Movie m, CGrafPtr port,
GDHandle gdh). After preparing the movie display window, it is
time to add the standard movie controller. MakeMovieController()
shows how to use the Component Manager to access the default
movie controller. The Movie Toolbox is then called upon to attach
the controller to the movie display window.
As mentioned earlier, components register their individual
capabilities with the system. At startup, the Component Manager
locates and registers all components in files of type 'thng'
located in the Extensions Folder under System 7, or in the System
Folder itself under System 6. An application can then request the
use of a reqistered component.
When an application wishes to use a component (which can
range in functionality from decompression components to media
handlers), it fills out a ComponentDescription record. The fields
of a ComponentDescription describe what capabilities are needed,
meaning you ask for a component by function rather than by name.
If the system locates a suitable component, its reference is
passed to the application. The reference itself does not provide
the application with immediate access to the component, however.
Pass the reference to OpenComponent(), which creates an instance
of the component. The component instance provides an application
with an open line of communication to the actual component, which
may be designed to be accessed multiple times simultaneously.
Components are classified by type. We request the basic
player component, the standard movie controller, by passing
"play" in the componentType field of the ComponentDescription.
The remainder of the ComponentDescription fields (SubType,
Manufacturer, Flags, and FlagsMask) are left blank. These fields
are used to further describe a particular component, should you
need access to a specific component. FindNextComponent() reads
the ComponentDescription and attempts to locate a corresponding
component with Component FindNextComponent (Component aComponent,
ComponentDescription *looking).
Passing 0 (zero) in aComponent tells the Component Manager
to return the first match it finds. During a more exhaustive
component search, you might pass the last component found back to
FindNextComponent() in which case the search will continue from
that component on, returning the next match in the component
list. Remember that this call alone does not return an
immediately accessible component. Use OpenComponent(), which
returns an instance to a component when passed a valid reference.
Once opened, the controller component is attached to the
movie display window. This is done with a call to
MCNewAttachedController(), passing the controller instance, the
movie and its display window (movieWindow), and a point
specifying the display window's upperleft corner. This function
associates the controller with our movie, attaching it to the
display window using ComponentResult MCNewAttachedController
(MovieController mc, Movie m,WindowPtr w, Point where).
We then resize the display window to accommodate the newly
attached controller. This minor adjustment is necessary, for the
display window is only large enough to display the movie itself
and no more. Resizing the window to take into account the
controller bounding box permits both controller and display
window to be visible at the same time. If you are not able to see
the controller when the movie is displayed, check to make sure
the display window has been correctly resized.
ShowMovieController() illustrates the proper steps.
After adjusting the display window to accommodate its new
controller, we prepare the movie for play. To ensure that the
movie starts playing at the very beginning, "rewind" the movie
with a call to GoToBeginningOfMovie().
PrerollMovie() instructs the Movie Toolbox to activate the
movie's media handlers prior to play time, allowing segments of
media data to be preloaded into memory. The overall effect is
smoother play, since disk access is reduced during playback.
Other techniques involve preloading specific portions of the
movie into RAM with calls to LoadMovieIntoRam(),
LoadTrackIntoRam(), and LoadMediaIntoRam(). These calls give a
noticeable performance increase when a movie demands significant
amounts of random access during playback with void
GoToBeginningOfMovie (Movie m) and OSErr PrerollMovie (Movie m,
TimeValue time, Fixed rate).
Since only "active" movies and tracks are serviced by the
Movie Toolbox, it is necessary to activate our entire movie with
a call to SetMovieActive(). A similar call, SetTrackEnabled(),
operates on individual tracks. A movie or track is made active by
passing TRUE, inactive by passing FALSE. The active status of a
movie or track can be checked with GetMovieActive() and
GetTrackEnabled().
void SetMovieActive (Movie m, Boolean active)
Having prepared the movie, we are ready to being play. A small
event-loop makes it possible to play and control the movie via
the movie controller component. While the movie is not finished
playing (!IsMovieDone()), we get the next event and pass it to
MCIsPayerEvent() which handles all events for a movie controller.
Normally called from within an application's main event loop,
MCIsPayerEvent() returns a long integer set to 1 if the
controller component handled the event, 0 (zero) otherwise. If
the controller component handles the event, your main event-loop
should skip the remainder of its event loop and wait for the next
event. If 0 (zero) is returned, your application should process
the event in its normal manner.
ComponentResult MCIsPlayerEvent(MovieController mc,const
EventRecord *e)
The movie controller component takes all the necessary actions
for supporting the movie controller and its associated movie.
All aspects of movie play are controlled through the controller
component, including volume control, update events, and
MultiFinder suspend and resume events. Your application need not
be concerned with advancing the movie during play, synchronizing
the sound track or even updating the movie window.
MCIsPayerEvent() ensures these concerns are handled by the movie
controller component when passed the corresponding event. If you
wish to alter or enhance this process, you may set up an action
filter which allows your application to perform custom processing
for a movie controller event. An action filter is installed with
a call to MCSetActionFilter().
Once our small event loop is entered, the movie is displayed
on screen. Click on the controller play button to set the movie
in motion . The movie will begin playing active tracks,
displaying video and playing sound if available. Use the
controller to fast forward, reverse direction of play, pause the
movie or change the volume. Once a movie has finished playing
(that is, once all track media have been played to end), its
storage is freed and references to the movie and controller are
closed.
The main loop is designed to repeat the entire movie playing
process until the cancel button in the Standard File Preview
dialog is selected. At that time, ExitMovies() is called.
ExitMovies() instructs the Movie Toolbox to release all private
storage allocated for your application when EnterMovies() was
called. You must be sure to close all open component connections
(movie controllers, sequence grabbers, etc.) prior to calling
ExitMovies(). If a System Error is generated when calling
ExitMovies(), it is likely that a component connection was not
properly closed or your movie was not disposed of prior to the
call.
OSErr CreateMovieFile (const FSSpec *fileSpec, OSType
creator, ScriptCode scriptTag, long createMovieFileFlags,
short *resRefNum, Movie *newMovie)
The creator parameter determines which application will be
launched when the movie file is double-clicked or opened from the
Finder. This is normally set to the creator code of the
application making the movie. Since MovieMaker can only create
movies, not play them, we pass the creator code for MoviePlayer
('MPLA').
To specify a specific script for a movie (used in
localization, the process of adapting an application to a
specific language or culture), use the scripTag parameter.
MovieMaker does not utilize scripts, and so passes 0 (zero) for
this parameter.
Various file creation options are available using the
createMovieFileFlags parameter. Passing
createMovieFileDeleteCurFile instructs the Movie Toolbox to
delete any existing file currently specified by the fileSpec
parameter before creating a new one. This ensures that our movie
file is created from scratch. Several options are available when
creating a new file, ranging from movie control to track access.
Consult Apple's QuickTime documentation for a detailed
description of this parameter.
If CreateMovieFile() is successful newMovie will point to a
movie in memory and resRefNum will contain the reference number
of its resource fork. If unsucessful, an appropriate OSErr is
returned by the function and newMovie is set to nil.
Track and Media
NewMovieTrack() combined with NewTrackMedia() add a single track
and associated media to our movie. NewMovieTrack() should be
followed by NewTrackMedia() since a movie track without a media
is possible but serves no purpose:
Track NewMovieTrack (Movie m, Fixed width, Fixed height, short
trackVolume)
Media NewTrackMedia (Track t, OSType mediaType, TimeScale
timeScale, Handle dataRef, OSType dataRefType)
The width and height parameters of NewMovieTrack() define the
track display rectangle in relation to the movie rectangle. When
creating a track that is not displayed on screen, such as a sound
track, pass 0 (zero) for both parameters. trackVolume specifies
the playback volume for a track, which should be set to 0 (zero)
if the track contains no sound.
"Media" refers to the actual data associated with a
particular track. Each track can reference only one media, which
may reside within the movie file itself or on a storage device
such as CD ROM, remote server volume, or disk. NewTrackMedia()
creates a media based on the supplied media data type (video,
animation, sound, etc.), time scale and data references. The
last two parameters, dataRef and dataRefType, specify where the
actual data samples for the media are stored. Since our samples
will be stored within the movie file itself, we pass nil for
both.
The new media has no data associated with it at this point.
Before adding the data (known as data samples or samples) we
must inform the Movie Toolbox of our intentions.
BeginMediaEdits() starts a media-editing session, notifying
the Movie Toolbox that we will be altering media. Adding samples
must be done within a media-editing session started by
BeginMediaEdits() and concluded with EndMediaEdits(). We will be
adding several samples to the media, each a compressed QuickDraw
image representing a frame in the completed movie.
Making Frames
MakeMovieGWorld() prepares the graphics enviroment, or GWorld,
into which we will draw our movie frames. A GWorld is an
offscreen port and pixel map with associated offscreen memory.
GWorlds are allocated based on a supplied pixel depth and
rectangle. We pass the deepest pixel depth our images might
have, along with the bounding rectandle of our largest movie
frame (which all happen to be the same size). Passing the
maximum of each ensures that the GWorld allocated for our movie,
movieGWorld, will be able to accomodate the largest individual
frame.
We also need to allocate storage for the compressed frames,
since the compression routine requires a location in memory to
place the compressed image when finished. AllocateMovieBuffer()
sets aside enough memory to hold a single compressed movie frame.
To calculate how much memory is needed, a call to
GetMaxCompresionSize() is made with parameters describing what
type of compressor component to use and to what depth and quality
the image should be compressed:
OSErr GetMaxCompressionSize (PixMapHandle src, const Rect *srcRect, short colorDepth, CodecQ quality, CodecType cType, CompressorComponent codec, long *size)
A handle to our movieGWorld pixMap is passed as the source (src)
image. The size and pixel depth of the source are used to
calculate the maximum possible size of the compressed image,
where srcRect specifies the portion of the source image to
compress. No image compression is actually done, only a maximum
compressed image size is calculated. Using the compressed image
size, we allocate a frame buffer (frameDatabitsH) capable of
holding the largest possible compressed movie frame.
For a detailed explaination of the various
GetMaxCompressionSize() arguments, see "Details, Details" in this
article.
Adding Frame Samples to the Media
After allocating our GWorld (movieGWorld ) and frame buffer
(frameDatabitsH ), we create the media samples. Our media
samples are the individual graphic frames which make up the
movie. [QuickTime 1.0 supports video, animation and sound
samples. Future versions of QuickTime are expected to
support custom defined data types, such as samples gathered from
special laboratory equipment.]
MakeMovieFrames() uses simple QuickDraw routines to create a
unique graphic image, or frame. Each frame is copied to the
display window, providing visual feedback for the user.
AddMovieFrame() is then called, adding the individual frame to
the media.
AddMovieFrame() compresses the current frame using
CompressImage(), the results of which are placed into the frame
buffer referenced by frameDatabitsH. The compressed image
in frameDatabitsH is added to the media using AddMediaSample().
This process is repeated for every frame.
OSErr CompressImage (PixMapHandle src, const Rect *srcRect,
CodecQ quality, CodecType
cType, ImageDescriptionHandle desc, Ptr data)
OSErr AddMediaSample (Media m, Handle dataIn, long inOffset,
unsigned long size, TimeValue durationPerSample,
SampleDescriptionHandle sampleDescriptionH, long
numberOfSamples, short
sampleFlags, TimeValue *sampleTime)
For more information on AddMediaSample() see "Details, Details".
There you will also find several CompressImage() arguments
defined, as this call and GetMaxCompresionSize() share many of
the same parameters.
After all samples are created, compressed and added to the media
it is time to close shop. Our media editing session is closed
with a call to EndMediaEdits(). The media is then added to the
movie track with InsertMediaIntoTrack() which specifies the track
into which to add the media, where the media segment should be
inserted (expressed in terms of the movie's time scale), the
starting point of the media segment (expressed in terms of the
the media's time scale), the media segment's duration and the
media rate (1.0 indicates the natural playback rate of the
media).
OSErr InsertMediaIntoTrack (Track t, TimeValue trackStart,
TimeValue mediaTime, TimeValue mediaDuration,
Fixed mediaRate)
Next, the movie, track and media are saved as a resource in the
movie file. AddMovieResource() is passed the movie to add, the
movie file to add it to, and the name we wish to give the
resulting resource. AddMovieResource() creates a resource of
type 'moov' in the resource fork of the file, returning its
resource id. An unnamed resource is created if nil is passed as
the name.
Preview
As a finishing touch we add a preview to our movie. As discussed
earlier, a preview allows the user to see a thumbnail image of a
selected file prior to opening it (see figure 3). Creating a
preview is simple, consisting of a single call:
OSErr MakeFilePreview (short resRefNum, ProgressProcRecordPtr
progress)
This routine expects a resource reference number from which it
will make a PICT preview. We pass the resource reference id of
our newly created 'moov' resource, obtained in a previous call to
AddMovieResource(). The second parameter to MakeFilePreview()
specifies a progress function used to keep the user informed of
the preview creation process. Pass -1 (negative one) to use
the system default or specify a custom progress routine of your
own.